package com.matrix.rpc.server;

import com.matrix.rpc.client.RpcClient;
import com.matrix.rpc.common.RpcDecoder;
import com.matrix.rpc.common.RpcEncoder;
import com.matrix.rpc.common.RpcRequest;
import com.matrix.rpc.common.RpcResponse;
import com.matrix.rpc.registry.ServiceRegistry;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.HashMap;
import java.util.Map;

public class RpcServer implements ApplicationContextAware,InitializingBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(RpcClient.class);

    private String serverAddress;

    private ServiceRegistry serviceRegistry;

    private Map<String, Object> handlerMap = new HashMap<>();

    public RpcServer(String serverAddress) {
        this.serverAddress = serverAddress;
    }

    //服务器绑定的地址和端口，由spring在构造本类时候，从配置文件传入
    public RpcServer(String serverAddress, ServiceRegistry serviceRegistry) {
        this.serverAddress = serverAddress;
        this.serviceRegistry = serviceRegistry;
        System.out.println("server address: " + serverAddress);

    }

    /**
     * 通过注解，讲标注又Rpc服务注解的业务类----接口和impl实现类
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        LOGGER.info("set Application Context start...");
        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        if (!serviceBeanMap.isEmpty()) {
            for (Object bean: serviceBeanMap.values()) {
                String interfaceName = bean.getClass().getAnnotation(RpcService.class).value().getName();
                LOGGER.info("interface name: " + interfaceName);
                handlerMap.put(interfaceName, bean);
            }
        }
    }
    /**
     * 再次启动netty服务，绑定handle流水线。
     * 1、接受请求数据反序列化得到请求request对象
     * 2、根据request对象中的参数，让RpcHandler从handleMap中找到对应的impl，调用指定的方法，获取返回结果。
     * 3、讲业务调用结果封装到response并序列化后发往客户端
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        LOGGER.info("after properties set...");
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline()
                                    .addLast(new RpcDecoder(RpcRequest.class)) //注册解码IN-1
                                    .addLast(new RpcEncoder(RpcResponse.class)) //注册编码OUT-1
                                    .addLast(new RpcHandler(handlerMap)); //注册RpcHandlerIN-2
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            String[] array = serverAddress.split(":");
            String host = array[0];
            int port = Integer.parseInt(array[1]);

            ChannelFuture future = bootstrap.bind(host,port).sync();
            LOGGER.info("server started on port " + port);
            if (serviceRegistry != null) {
                serviceRegistry.register(serverAddress);
            }
            future.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }


}
